#pragma once 
#include <semaphore.h>
#include <vector>
using namespace std;

template<class T>
class RingQueue
{
    static const size_t defaultCap = 5;
private:
    void P(sem_t& x)
    {
        sem_wait(&x);
    }
    
    void V(sem_t& x)
    {
        sem_post(&x);
    }

    void Lock(pthread_mutex_t& mutex)
    {
        pthread_mutex_lock(&mutex);
    }

    void UnLock(pthread_mutex_t& mutex)
    {
        pthread_mutex_unlock(&mutex);
    }
public:
    RingQueue(size_t capacity = defaultCap) 
    : _ringQueue(capacity)
    , _capacity(capacity)
    , _cPos(0)
    , _pPos(0)
    {
        // cout << "capacity:" << capacity << endl;
        sem_init(&_cDataSem, 0, 0);
        sem_init(&_pSpaceSem, 0, capacity);
    }

    ~RingQueue()
    {
        sem_destroy(&_cDataSem);
        sem_destroy(&_pSpaceSem);
    }

    void Push(const T& x) 
    {        
        P(_pSpaceSem);

        Lock(_pMutex);          // 加锁和解锁在PV操作之后，提高并发度。只有当信号量允许时，进程才会尝试获取锁。这样可以减少锁的争用，提高系统的整体效率。
        _ringQueue[_pPos] = x;
        _pPos++;    
        _pPos %= _capacity;     // 维持队列的环形特征
        UnLock(_pMutex);

        V(_cDataSem);

    }
        

    void Pop(T* x)
    {
        P(_cDataSem);
        Lock(_cMutex);          // 加锁和解锁在PV操作之后，提高并发度。只有当信号量允许时，进程才会尝试获取锁。这样可以减少锁的争用，提高系统的整体效率。
        *x = _ringQueue[_cPos];
        _cPos++;
        _cPos %= _capacity;     // 维持队列的环形特征
        UnLock(_cMutex);
        V(_pSpaceSem);
    }

private:
    vector<T> _ringQueue;    // 用数组模拟环形队列
    size_t _capacity;        // 队列大小
    size_t _cPos;            // 消费者下标
    size_t _pPos;            // 生产者下标
    sem_t _cDataSem;         // 消费者关心还有多少剩余数据
    sem_t _pSpaceSem;        // 生产者关心还有多少剩余空间
    pthread_mutex_t _cMutex; // 消费者的锁
    pthread_mutex_t _pMutex; // 生产者的锁
};

